/***************************************************************************
 **
 **  Copyright (C) 2021 MaMinJie <canpool@163.com>
 **  Contact: https://github.com/canpool
 **           https://gitee.com/icanpool
 **
 **  GNU Lesser General Public License Usage
 **  Alternatively, this file may be used under the terms of the GNU Lesser
 **  General Public License version 3 as published by the Free Software
 **  Foundation and appearing in the file LICENSE.LGPL3 included in the
 **  packaging of this file. Please review the following information to
 **  ensure the GNU Lesser General Public License version 3 requirements
 **  will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
 **
 **  GNU General Public License Usage
 **  Alternatively, this file may be used under the terms of the GNU
 **  General Public License version 2.0 or (at your option) the GNU General
 **  Public license version 3 or any later version approved by the KDE Free
 **  Qt Foundation. The licenses are as published by the Free Software
 **  Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
 **  included in the packaging of this file. Please review the following
 **  information to ensure the GNU General Public License requirements will
 **  be met: https://www.gnu.org/licenses/gpl-2.0.html and
 **  https://www.gnu.org/licenses/gpl-3.0.html.
 **
****************************************************************************/
#include "literibbonbar.h"
#include "literibbonpage.h"
#include "literibbontabbar.h"
#include "litequickaccessbar.h"
#include "liteapplicationbutton.h"
#include "litetoolbar.h"
#include "litebar.h"

#include <QStyle>
#include <QList>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMouseEvent>
#include <QApplication>
#include <QStackedWidget>
#include <QToolButton>
#include <QLabel>
#include <QDebug>

QLITE_BEGIN_NAMESPACE

/* LiteRibbonStackedWidget */
class LiteRibbonStackedWidget : public QStackedWidget
{
    Q_OBJECT
public:
    explicit LiteRibbonStackedWidget(LiteRibbonTabBar *tabBar, QWidget *parent = Q_NULLPTR);
    virtual ~LiteRibbonStackedWidget() {}

    void setPopupMode(bool popup);
    bool isPopupMode() const;

protected:
    virtual bool event(QEvent* event);

private:
    LiteRibbonTabBar *m_tabBar;
};

LiteRibbonStackedWidget::LiteRibbonStackedWidget(LiteRibbonTabBar *tabBar, QWidget *parent)
    : QStackedWidget(parent)
    , m_tabBar(tabBar)
{
}

void LiteRibbonStackedWidget::setPopupMode(bool popup)
{
    setMouseTracking(popup);
    if (popup) {
        setWindowFlags(Qt::Popup);
    } else {
        setWindowFlags(Qt::Widget);
    }
}

bool LiteRibbonStackedWidget::isPopupMode() const
{
    return (windowFlags() & Qt::Popup);
}

bool LiteRibbonStackedWidget::event(QEvent *event)
{
    switch (event->type()) {
        case QEvent::MouseButtonDblClick:
        case QEvent::MouseButtonPress: {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            QWidget* widget = QApplication::widgetAt(mouseEvent->globalPos());
            if (widget == m_tabBar) {
                const QPoint localPos = widget->mapFromGlobal(mouseEvent->globalPos());
                if (m_tabBar->tabAt(localPos) == -1) {
                    break;
                }
                // send hover leave event to tabbar, otherwise, when you click tab1 to display the popup stack,
                // when you click another tabX again, tab1 is still in hover state
                QHoverEvent hle(QEvent::HoverLeave, m_tabBar->mapToGlobal(QCursor::pos()),
                               m_tabBar->mapToGlobal(QCursor::pos()));
                QApplication::sendEvent(m_tabBar, &hle);

                // post event to tabbar
                QMouseEvent *me = new QMouseEvent(mouseEvent->type(), localPos, mouseEvent->globalPos(),
                    mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers());
                QApplication::postEvent(widget, me);
                return true;
            }
        }
        break;
        default:
        break;
    }
    return QStackedWidget::event(event);
}

class LiteRibbonBarPrivate : public QObject
{
    Q_OBJECT
public:
    LiteRibbonBarPrivate();

    void init();
    void setRibbonStyle(LiteRibbonBar::RibbonStyle style);

    void insertPage(int index, LiteRibbonPage *page);
    void removePage(int index);
    int pageIndex(LiteRibbonPage *page) const;

    bool validateIndex(int index) const
    { return index >= 0 && index < m_listPages.count(); }

    void showMinMaximized();
    void showPopupStack();

    void cleanLayout(QLayout *layout);

public Q_SLOTS:
    void slotCurrentTabChanged(int index);
    void slotTabBarClicked(int index);
    void slotTabBarDoubleClicked(int index);

public:
    LiteRibbonBar *q;
    LiteRibbonBar::RibbonStyle m_style;

    LiteBar *m_liteBar;
    QWidget *m_titleWidget;
    LiteRibbonStackedWidget *m_stack;
    LiteRibbonTabBar *m_tabBar;
    LiteQuickAccessBar *m_quickAccessBar;
    LiteApplicationButton *m_appButton;
    LiteToolBar *m_topToolBar;
    LiteToolBar *m_bottomToolBar;
    QHBoxLayout *m_topLayout;
    QHBoxLayout *m_bottomLayout;
    QVBoxLayout *m_mainLayout;
    QWidget *m_mainWidget;

    QList<LiteRibbonPage *> m_listPages;
    bool m_minimized;
    bool m_frameless;
};

LiteRibbonBarPrivate::LiteRibbonBarPrivate()
    : m_style(LiteRibbonBar::ClassicStyle)
    , m_mainWidget(Q_NULLPTR)
    , m_minimized(false)
    , m_frameless(false)
{

}

void LiteRibbonBarPrivate::init()
{
    m_titleWidget = new QWidget();

    m_liteBar = new LiteBar(m_mainWidget);
    m_mainWidget->removeEventFilter(m_liteBar);
    m_liteBar->setTitleBar(m_titleWidget);

    m_tabBar = new LiteRibbonTabBar();

    m_stack = new LiteRibbonStackedWidget(m_tabBar, q);
    m_stack->setAutoFillBackground(true);
    m_stack->setBackgroundRole(QPalette::Mid);
    m_stack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_stack->setFixedHeight(100);

    m_quickAccessBar = new LiteQuickAccessBar();

    m_appButton = new LiteApplicationButton(QObject::tr("App"));
    m_appButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);

    const int sz = q->style()->pixelMetric(QStyle::PM_SmallIconSize, Q_NULLPTR, q);
    m_topToolBar = new LiteToolBar();
    m_topToolBar->setIconSize(QSize(sz, sz));
    m_bottomToolBar = new LiteToolBar();
    m_bottomToolBar->setIconSize(QSize(sz, sz));

    m_topLayout = new QHBoxLayout();
    m_topLayout->setContentsMargins(1, 0, 0, 0);
    m_topLayout->setSpacing(0);

    m_bottomLayout = new QHBoxLayout();
    m_bottomLayout->setMargin(0);
    m_bottomLayout->setSpacing(0);

    QVBoxLayout *titleLayout = new QVBoxLayout();
    titleLayout->setMargin(0);
    titleLayout->setSpacing(0);
    titleLayout->addLayout(m_topLayout);
    titleLayout->addLayout(m_bottomLayout);
    m_titleWidget->setLayout(titleLayout);

    m_mainLayout = new QVBoxLayout();
    m_mainLayout->setMargin(0);
    m_mainLayout->setSpacing(0);
    m_mainLayout->addWidget(m_titleWidget);
    m_mainLayout->addWidget(m_stack);
    q->setLayout(m_mainLayout);

    setRibbonStyle(m_style);

    connect(m_tabBar, SIGNAL(currentChanged(int)), m_stack, SLOT(setCurrentIndex(int)));
    connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabChanged(int)));
    connect(m_tabBar, SIGNAL(tabBarClicked(int)), this, SLOT(slotTabBarClicked(int)));
    connect(m_tabBar, SIGNAL(tabBarDoubleClicked(int)), this, SLOT(slotTabBarDoubleClicked(int)));
}

/**
 * @brief LiteRibbonBarPrivate::setRibbonStyle
 * @param style
 *
 * ClassicStyle:
 *  ------------------------------------------------------------------------------------
 *  | *Logo | QuickAccessBar |          *Title          | TopToolBar | *SysButtonGroup |
 *  ------------------------------------------------------------------------------------
 *  | AppButton |                        TabBar               |   BottomToolBar        |
 *  ------------------------------------------------------------------------------------
 *  |                                    STACK                                         |
 *  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
 *
 * MergedStyle:
 *  ------------------------------------------------------------------------------------
 *  | *Logo | AppButton | TabBar | *Title | QuickAccessBar | ToolBar | *SysButtonGroup |
 *  ------------------------------------------------------------------------------------
 *  |                                    STACK                                         |
 *  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~------------------~~~|
 *
 * 1) *Logo,*Title,*SysButtonGroup -- Customize TitleBar elements
 * 2) ToolBar - TopToolBar, BottomToolBar
 *
 */
void LiteRibbonBarPrivate::setRibbonStyle(LiteRibbonBar::RibbonStyle style)
{
    cleanLayout(m_topLayout);
    cleanLayout(m_bottomLayout);

    if (style == LiteRibbonBar::ClassicStyle) {
        if (m_frameless) {
            m_topLayout->addWidget(m_liteBar->logoButton());
        }
        m_topLayout->addWidget(m_quickAccessBar);
        m_topLayout->addStretch();
        if (m_frameless) {
            m_topLayout->addWidget(m_liteBar->titleLabel());
            m_topLayout->addStretch();
        }
        m_topLayout->addWidget(m_topToolBar);
        if (m_frameless) {
            m_topLayout->addWidget(m_liteBar->sysToolBar());
        }

        m_bottomLayout->addWidget(m_appButton);
        m_bottomLayout->addWidget(m_tabBar);
        m_bottomLayout->addStretch();
        m_bottomLayout->addWidget(m_bottomToolBar);
    } else if (style == LiteRibbonBar::MergedStyle) {
        if (m_frameless) {
            m_topLayout->addWidget(m_liteBar->logoButton());
        }
        m_topLayout->addWidget(m_appButton);
        m_topLayout->addWidget(m_tabBar);
        m_topLayout->addStretch();
        if (m_frameless) {
            m_topLayout->addWidget(m_liteBar->titleLabel());
            m_topLayout->addStretch();
        }
        m_topLayout->addWidget(m_quickAccessBar);
        m_topLayout->addWidget(m_topToolBar);
        m_topLayout->addWidget(m_bottomToolBar);
        if (m_frameless) {
            m_topLayout->addWidget(m_liteBar->sysToolBar());
        }
    }

    if (!m_liteBar->logoButton()->icon().isNull()) {
        m_liteBar->logoButton()->setVisible(m_frameless);
    }
    m_liteBar->titleLabel()->setText(m_mainWidget->windowTitle());
    m_liteBar->titleLabel()->setVisible(m_frameless);
    m_liteBar->sysToolBar()->setVisible(m_frameless);
}

void LiteRibbonBarPrivate::insertPage(int index, LiteRibbonPage *page)
{
    if (validateIndex(index)) {
        m_listPages.insert(index, page);
        m_stack->insertWidget(index, page);
    } else {
        index = m_listPages.count();
        m_listPages.append(page);
        m_stack->addWidget(page);
    }
}

void LiteRibbonBarPrivate::removePage(int index)
{

}

int LiteRibbonBarPrivate::pageIndex(LiteRibbonPage *page) const
{
    return m_listPages.indexOf(page);
}

void LiteRibbonBarPrivate::showMinMaximized()
{
    m_minimized = !m_minimized;

    if (m_minimized) {
        m_stack->hide();
        m_mainLayout->removeWidget(m_stack);
        m_stack->setPopupMode(true);
        showPopupStack();
    } else {
        m_stack->hide();
        m_stack->setPopupMode(false);
        m_mainLayout->addWidget(m_stack);
        m_stack->show();
    }
}

void LiteRibbonBarPrivate::showPopupStack()
{
    QPoint localPos = QPoint(q->x(), m_tabBar->y() + m_tabBar->height() + 1);
    QPoint globalPos = q->mapToGlobal(localPos);
    m_stack->setGeometry(globalPos.x(), globalPos.y(), q->width(), m_stack->height());

    if (m_stack->isHidden()) {
        m_stack->show();
    }
}

void LiteRibbonBarPrivate::cleanLayout(QLayout *layout)
{
    QLayoutItem *child;
    while ((child = layout->takeAt(0)) != nullptr) {
        if (child->widget()) {
            child->widget()->setParent(nullptr);
        } else if(child->layout()){
            cleanLayout(child->layout());
        }
    }
}

void LiteRibbonBarPrivate::slotCurrentTabChanged(int index)
{
    Q_UNUSED(index);
    if (m_minimized) {
        showPopupStack();
    }
}

void LiteRibbonBarPrivate::slotTabBarClicked(int index)
{
    if (index == m_tabBar->currentIndex()) {
        // Since clicking the same tab will not trigger a changed signal,
        // when the ribbon is minimized and the stack is hidden, the stack cannot be displayed
        // so need to show stack
        if (m_minimized && m_stack->isHidden()) {
            showPopupStack();
        }
    }
}

void LiteRibbonBarPrivate::slotTabBarDoubleClicked(int index)
{
    Q_UNUSED(index);
    showMinMaximized();
}

LiteRibbonBar::LiteRibbonBar(QWidget *parent)
    : QWidget(parent)
    , d(new LiteRibbonBarPrivate())
{
    Q_ASSERT(parent);
    d->q = this;
    d->m_mainWidget = parent;
    d->init();
    setObjectName(QLatin1String("qlite_ribbonbar"));
}

LiteRibbonBar::~LiteRibbonBar()
{
    delete d;
}

LiteRibbonPage *LiteRibbonBar::addPage(const QString &text)
{
    return insertPage(-1, text);
}

LiteRibbonPage *LiteRibbonBar::addPage(const QIcon &icon, const QString &text)
{
    return insertPage(-1, icon, text);
}

void LiteRibbonBar::addPage(LiteRibbonPage *page)
{
    insertPage(-1, page);
}

LiteRibbonPage *LiteRibbonBar::insertPage(int index, const QString &text)
{
    return insertPage(index, QIcon(), text);
}

LiteRibbonPage *LiteRibbonBar::insertPage(int index, const QIcon &icon, const QString &text)
{
    int idxTab = d->m_tabBar->insertTab(index, icon, text);
    LiteRibbonPage *page = new LiteRibbonPage(this);
    d->insertPage(idxTab, page);
    page->setTitle(text);
    return page;
}

void LiteRibbonBar::insertPage(int index, LiteRibbonPage *page)
{
    Q_ASSERT(page != Q_NULLPTR);
    if (d->pageIndex(page) != -1) {
        qWarning("LiteRibbonBar::insertPage: Can't add page twice.");
        return;
    }
    page->setParent(this);
    int idxTab = d->m_tabBar->insertTab(index, page->title());
    d->insertPage(idxTab, page);
}

void LiteRibbonBar::movePage(LiteRibbonPage *page, int newIndex)
{

}

void LiteRibbonBar::movePage(int index, int newIndex)
{

}

void LiteRibbonBar::removePage(LiteRibbonPage *page)
{

}

void LiteRibbonBar::removePage(int index)
{

}

void LiteRibbonBar::detachPage(LiteRibbonPage *page)
{

}

void LiteRibbonBar::detachPage(int index)
{

}

void LiteRibbonBar::clearPages()
{

}

LiteRibbonPage *LiteRibbonBar::page(int index) const
{
    return Q_NULLPTR;
}

int LiteRibbonBar::pageCount() const
{
    return -1;
}

int LiteRibbonBar::currentPageIndex() const
{
    return -1;
}

QList<LiteRibbonPage *> LiteRibbonBar::pages() const
{
    QList<LiteRibbonPage *> lst;
    return lst;
}

LiteQuickAccessBar *LiteRibbonBar::quickAccessBar() const
{
    return d->m_quickAccessBar;
}

LiteRibbonBar::RibbonStyle LiteRibbonBar::ribbonStyle() const
{
    return d->m_style;
}

void LiteRibbonBar::setRibbonStyle(LiteRibbonBar::RibbonStyle style)
{
    if (d->m_style == style) {
        return;
    }
    d->m_style = style;
    d->setRibbonStyle(style);
}

LiteToolBar *LiteRibbonBar::toolBar(LiteRibbonBar::ToolBarPosition position) const
{
    if (LiteRibbonBar::ToolBarTopPosition == position) {
        return d->m_topToolBar;
    } else {
        return d->m_bottomToolBar;
    }
}

LiteBar *LiteRibbonBar::liteBar() const
{
    return d->m_liteBar;
}

void LiteRibbonBar::updateParentFlags()
{
    QWidget *pw = d->m_mainWidget;
    if (pw == Q_NULLPTR) {
        return;
    }
    d->m_frameless = (pw->windowFlags() & Qt::FramelessWindowHint);
    if (d->m_frameless) {
        d->m_mainWidget->installEventFilter(d->m_liteBar);
    } else {
        d->m_mainWidget->removeEventFilter(d->m_liteBar);
    }
    d->m_liteBar->updateWidgetFlags();
    d->setRibbonStyle(d->m_style);
}

QLITE_END_NAMESPACE

#include "literibbonbar.moc"
